package com.ruoyi.framework.mybatis;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.ruoyi.common.sequence.DbSeqBuilder;
import com.ruoyi.common.sequence.core.Sequence;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;

public class JdbcIdentifierGenerator implements IdentifierGenerator {
	
	private static final Logger logger = LoggerFactory.getLogger(JdbcIdentifierGenerator.class);
	
	private DataSource dataSource;
	
	private String prefix;
	
	private String tableName;
	
	public JdbcIdentifierGenerator(DataSource dataSource) {
		this(dataSource, "seq_", "ruoyi");
	}
	
	public JdbcIdentifierGenerator(DataSource dataSource, String prefix, String tableName) {
		this.dataSource = dataSource;
		this.prefix = prefix;
		this.tableName = tableName;
	}
	
	private static final Map<String, Sequence> sequenceCache = new ConcurrentHashMap<>(10);
	
	private static final Lock lock = new ReentrantLock();
	
	@Override
	public Number nextId(Object entity) {
		String bizKey = null;
		
		if(entity == null) {
			bizKey = "system_default"; // 默认全局ID
		} else {
			Class<? extends Object> entityClass = entity.getClass();
			
			// 默认以当前传入的class全类名来作为bizKey生成分布式ID
			String className = entityClass.getName();
			bizKey = StrUtil.subAfter(className, ".", true);
			
			TableName annotation = entityClass.getAnnotation(TableName.class);
			if(annotation!=null) { // 如果实体类上增加了TableName注解则以该注解来作为bizKey
				bizKey = annotation.value();
			}
		}
		
		Sequence sequence = null;
		
		if(!sequenceCache.containsKey(bizKey)) { // 校验是否已创建序列器
			logger.debug("序列器不存在：{}", bizKey);
			
			lock.lock();
			try {
				if(!sequenceCache.containsKey(bizKey)) { // 二次校验，防止多个线程重复创建序列器
					logger.debug("创建序列器：{}，时间：{}", bizKey, DateUtil.now());
					
					DbSeqBuilder builder = DbSeqBuilder.create();
					builder.dataSource(dataSource);
					builder.step(100);
					builder.stepStart(1);
					builder.retryTimes(3);
					builder.prefix(prefix);
					builder.tableName(tableName);
					
					builder.bizName(bizKey);
					
					sequence = builder.build();
					
					sequenceCache.putIfAbsent(bizKey, sequence);
				} else {
					sequence = sequenceCache.get(bizKey);
				}
			} finally {
				lock.unlock();
			}
			
		} else {
			sequence = sequenceCache.get(bizKey);
		}
		
		return sequence.nextValue();
	}

	public DataSource getDataSource() {
		return dataSource;
	}

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	public String getPrefix() {
		return prefix;
	}

	public void setPrefix(String prefix) {
		this.prefix = prefix;
	}

	public String getTableName() {
		return tableName;
	}

	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

}
